/**
 * @format
 * @Author: TC
 * @Date: 2025-05-28 14:46:04
 * @Last Modified by: TC
 * @Last Modified time: 2025-06-21 19:29:25
 */

import archiver from "archiver";
import fs, { createWriteStream } from "fs";
import path from "path";
import XLSX from "xlsx";
import { ExcelUtil } from "./ExcelUtil";
import { FileUtil } from "./FileUtil";
import { Tip } from "./Tip";
import { IConfig, ISubGame } from "./default/Interface";

class ConfigManager {
    private _rootDir = "";
    private _bundleName = "";
    private _gameList: ISubGame[] = [];
    private _tempPath = "";
    private _tempJsonPath = "";
    private _proPath = path.resolve(__dirname, "../../../../assets");
    private _worksheetList: Map<string, XLSX.WorkSheet> = new Map();
    constructor() {
        const data = fs.readFileSync(path.resolve(__dirname, "../../config/config.json"), "utf8");
        const jsonData = JSON.parse(data) as IConfig;
        this._rootDir = path.resolve(__dirname, "../../../../excels");
        const tempPath = path.resolve(__dirname, "../../temp");
        if (!fs.existsSync(tempPath)) {
            fs.mkdirSync(tempPath);
        }
    }
    public exportCfg(): void {
        this._export();
        // this._copy();
    }

    private async _copy() {
        const srcTs = "db://assets/StaticRes/Templete/Templete.ts";
        const dstTs = "db://assets/Bundles/Module/Clone/Clone.ts";
        const srcTsUuid = await Editor.Message.request("asset-db", "query-uuid", srcTs);
        const srcCompressUUID = Editor.Utils.UUID.compressUUID(String(srcTsUuid), false);

        const tsInfo = await Editor.Message.request("asset-db", "copy-asset", srcTs, dstTs, {
            overwrite: true,
            rename: true,
        });
        const uuid = tsInfo?.uuid;
        const compressUUID = Editor.Utils.UUID.compressUUID(String(uuid), false);
        console.log(srcTsUuid, srcCompressUUID);
        let content = fs.readFileSync(path.join(this._proPath, "Bundles", "Module", "Clone", "Clone.ts"), "utf-8");
        content = content.replace("Templete", "Clone");
        fs.writeFileSync(path.join(this._proPath, "Bundles", "Module", "Clone", "Clone.ts"), content);

        const srcPfb = "db://assets/StaticRes/Templete/TempleteUI.prefab";
        const dstPfb = "db://assets/Bundles/Module/Clone/CloneUI.prefab";
        await Editor.Message.request("asset-db", "copy-asset", srcPfb, dstPfb, {
            overwrite: true,
            rename: true,
        });

        console.log(srcCompressUUID);
        let contentPfb = fs.readFileSync(path.join(this._proPath, "Bundles", "Module", "Clone", "CloneUI.prefab"), "utf-8");
        contentPfb = contentPfb.replace(srcCompressUUID, compressUUID);
        fs.writeFileSync(path.join(this._proPath, "Bundles", "Module", "Clone", "CloneUI.prefab"), contentPfb);

        await Editor.Message.request("scene", "save-scene");
    }

    private _export(): void {
        this._worksheetList.clear();
        const path = `${this._rootDir}`;
        this._initTempDir();
        this._loopDir(path, (fullPath) => {
            this._excelToJson(fullPath);
        });
        // return;
        if (!this._exportTs(this._worksheetList)) {
            return;
        }
        // return;
        this._compress(this._copyBinToProject.bind(this));
    }

    private _loopDir(dir: string, cb: (fullPath: string) => void): void {
        const entries = fs.readdirSync(dir, { withFileTypes: true });
        const filePaths: string[] = [];
        for (const entry of entries) {
            const fullPath = path.join(dir, entry.name);

            if (entry.isDirectory()) {
                // 递归遍历子目录
                this._loopDir(fullPath, cb);
            } else if (entry.isFile()) {
                // 添加文件路径
                filePaths.push(fullPath);
                if (fullPath.endsWith(".xls") && !fullPath.includes("~")) {
                    cb(fullPath);
                }
            }
        }
    }

    private _getSubgameByName(gameName: string): ISubGame {
        let subgame: ISubGame = {} as ISubGame;
        this._gameList.forEach((game) => {
            if (game.nameEN === gameName) {
                subgame = game;
            }
        });
        return subgame;
    }

    private cnt = 0;
    private _initTempDir(): void {
        this._tempPath = path.resolve(__dirname, "../../temp", this._bundleName);
        if (!fs.existsSync(this._tempPath)) {
            fs.mkdirSync(this._tempPath);
        }
        this._emptyDirectory(this._tempPath);
        this._tempJsonPath = path.join(this._tempPath, "json");
        if (!fs.existsSync(this._tempJsonPath)) {
            fs.mkdirSync(this._tempJsonPath);
        }
        // console.log("init temp dir:", this._tempPath);
    }
    private _deleteExlCols(worksheet: XLSX.WorkSheet, deleteCols: number[]): XLSX.WorkSheet {
        const range = XLSX.utils.decode_range(worksheet["!ref"] || "A1:A1");
        // 转换列索引为列号
        const columnsToDelete = [...deleteCols.map((num) => XLSX.utils.encode_col(num))];
        // 转换列索引为列号（0-based）并排序
        const columnsToDeleteIndices = columnsToDelete.map((col) => XLSX.utils.decode_col(col)).sort((a, b) => a - b);
        const remainingColumns = [];
        for (let c = range.s.c; c <= range.e.c; c++) {
            if (!columnsToDeleteIndices.includes(c)) {
                remainingColumns.push(c);
            }
        }
        // console.log(remainingColumns);
        // 创建映射表
        const columnMap = new Map<number, number>();
        remainingColumns.forEach((oldCol, newColIndex) => {
            columnMap.set(oldCol, newColIndex);
        });

        // 创建新的工作表对象
        const newWorksheet: XLSX.WorkSheet = Object.assign({}, worksheet);
        newWorksheet["!ref"] = XLSX.utils.encode_range({
            s: { r: range.s.r, c: 0 },
            e: { r: range.e.r, c: remainingColumns.length - 1 },
        });

        // 清空单元格数据
        for (const key in newWorksheet) {
            if (key.match(/^[A-Z]+\d+$/)) {
                delete newWorksheet[key];
            }
        }

        // 移动剩余列到新位置
        for (const oldCol of remainingColumns) {
            const newCol = columnMap.get(oldCol)!;

            // 遍历每一行
            for (let r = range.s.r; r <= range.e.r; r++) {
                const oldCellRef = XLSX.utils.encode_cell({ r, c: oldCol });
                const newCellRef = XLSX.utils.encode_cell({ r, c: newCol });

                if (worksheet[oldCellRef]) {
                    newWorksheet[newCellRef] = worksheet[oldCellRef];
                }
            }
        }

        // 更新列宽设置
        if (worksheet["!cols"] && remainingColumns.length > 0) {
            newWorksheet["!cols"] = remainingColumns.map((oldCol) => worksheet["!cols"]![oldCol]);
        } else {
            delete newWorksheet["!cols"];
        }
        return newWorksheet;
    }

    private _deleteExlRows(worksheet: XLSX.WorkSheet, deleteRows: number[]): XLSX.WorkSheet {
        const range = XLSX.utils.decode_range(worksheet["!ref"] || "A1:A1");
        // 转换行号为0-based索引并排序（降序，避免删除后索引变化）
        const rowsToDelete = deleteRows
            .map((row) => row - 1) // 转换为0-based索引
            .sort((a, b) => b - a);

        // 创建新的工作表对象
        const newWorksheet: XLSX.WorkSheet = Object.assign({}, worksheet);
        newWorksheet["!ref"] = XLSX.utils.encode_range({
            s: { r: range.s.r, c: range.s.c },
            e: { r: range.e.r - rowsToDelete.length, c: range.e.c },
        });

        // 清空单元格数据
        for (const key in newWorksheet) {
            if (key.match(/^[A-Z]+\d+$/)) {
                delete newWorksheet[key];
            }
        }

        // 记录哪些行被删除
        const isRowDeleted = new Set(rowsToDelete);

        // 移动剩余行到新位置
        let newRowIndex = range.s.r;
        for (let r = range.s.r; r <= range.e.r; r++) {
            if (!isRowDeleted.has(r)) {
                const rowOffset = newRowIndex - r;

                // 遍历每一列
                for (let c = range.s.c; c <= range.e.c; c++) {
                    const oldCellRef = XLSX.utils.encode_cell({ r, c });
                    const newCellRef = XLSX.utils.encode_cell({ r: newRowIndex, c });

                    if (worksheet[oldCellRef]) {
                        newWorksheet[newCellRef] = worksheet[oldCellRef];
                    }
                }

                newRowIndex++;
            }
        }

        return newWorksheet;
    }
    private _printWorksheet(worksheet: XLSX.WorkSheet) {
        const range = XLSX.utils.decode_range(worksheet["!ref"] || "A1:A1");
        for (let r = range.s.r; r <= range.e.r; r++) {
            for (let c = range.s.c; c <= range.e.c; c++) {
                const cell = worksheet[XLSX.utils.encode_cell({ c, r })];
                if (cell) {
                    // console.log(cell.v);
                }
            }
        }
    }
    private _emptyDirectory(dirPath: string) {
        // 验证目录是否存在
        if (!fs.existsSync(dirPath)) {
            return;
        }

        // 读取目录内容
        const entries = fs.readdirSync(dirPath, { withFileTypes: true });

        // 处理每个条目
        for (const entry of entries) {
            const entryPath = path.join(dirPath, entry.name);
            if (entry.isDirectory()) {
                this._emptyDirectory(entryPath);
            } else {
                // 删除文件
                fs.unlinkSync(entryPath);
            }
        }
    }

    private _excelToJson(fullPath: string): void {
        let content = "";
        const excelPath = fullPath;
        // 读取Excel文件
        const workbook = XLSX.readFile(excelPath);

        // 获取工作表
        const sheetNames = workbook.SheetNames;
        console.log("sheetNames", sheetNames);
        sheetNames.forEach((sheetName) => {
            content = "";
            const worksheet = workbook.Sheets[sheetName];

            if (!worksheet) {
                throw new Error(sheetName ? `找不到工作表: ${sheetName}` : "Excel文件中没有工作表");
            }
            let name = ExcelUtil.getRowInSheet(worksheet, 0)[0];
            let newWorksheetTemp = this._deleteExlRows(worksheet, [1]);
            let types = ExcelUtil.getRowInSheet(newWorksheetTemp, 2);
            this._worksheetList.set(sheetName, newWorksheetTemp);
            let newWorksheet = this._deleteExlRows(worksheet, [1, 2, 4]);
            // console.log(newWorksheet);
            const range = XLSX.utils.decode_range(newWorksheet["!ref"] || "A1:A1");
            let keys = ExcelUtil.getRowInSheet(newWorksheet, 0);
            // console.log(types);
            // return;
            content += '{\n\t"title": [\n';
            for (let i = 0; i < keys.length; i++) {
                let key = keys[i];
                content += '\t\t"' + key + '"';
                if (i != keys.length - 1) content += ",\n";
                else content += "\n";
            }
            content += "\t],\n";
            content += '\t"content":[ \n';
            let len = range.e.r + 1;
            // return;
            // console.log("len", len);
            for (let i = 1; i < len; i++) {
                let vals = ExcelUtil.getRowInSheet(newWorksheet, i);
                // console.log("vals", vals);
                content += "\t\t[\n";

                for (let j = 0; j < vals.length; j++) {
                    let val: any = vals[j];
                    let type = types[j];
                    // if (types[j] != "int" && types[j] != "number") {
                    //     val = '"' + val + '"';
                    // } else {
                    //     val = val;
                    // }
                    if ("int" == type) {
                        val = val;
                    } else if ("number" == type) {
                        val = val;
                    } else if ("string" == type) {
                        val = '"' + val + '"';
                    } else if ("number[]" == type) {
                        const sp = String(val).split(",");
                        val = "[";
                        sp.forEach((v: string, i: number) => {
                            val += `${v}`;
                            if (i < sp.length - 1) {
                                val += ",";
                            }
                        });
                        val += "]";
                    } else {
                        val = '"' + val + '"';
                    }

                    content += "\t\t\t" + val;
                    if (j == vals.length - 1) {
                        content += "\n\t\t]";
                    } else {
                        content += ",\n";
                    }
                }
                if (i == len - 1) {
                    content += "\t\t\t\n";
                } else {
                    content += ",\n";
                }
            }
            content += "\t]\n}";
            // console.log(content);
            this._saveJson(sheetName, content);
        });
    }

    private _saveJson(cfgName: string, csv: string) {
        // console.log(this._tempCsvPath, this._tempCsvPath);
        const outputPath = path.join(this._tempJsonPath, `${cfgName}.json`);
        // console.log(outputPath);
        fs.writeFileSync(outputPath, csv);
    }
    private _compress(cb: () => void): void {
        const outputPath = path.join(this._tempPath, "jsonCfg.bin");
        // 创建ZIP文件流
        const output = createWriteStream(outputPath);
        const archive = archiver("zip", { zlib: { level: 9 } });

        // 处理错误
        output.on("error", (err) => {
            throw new Error(`创建ZIP文件失败: ${err.message}`);
        });

        archive.on("error", (err) => {
            throw new Error(`压缩过程出错: ${err.message}`);
        });

        // 添加目录到ZIP
        archive.directory(this._tempJsonPath, false);

        // 完成压缩
        archive.pipe(output);
        archive.finalize().then(() => {
            cb();
        });
    }

    private _copyBinToProject(): void {
        const cfgPath = path.resolve(this._proPath, "Bundles", "Config");
        if (!fs.existsSync(cfgPath)) {
            Tip.error(`找不到目录:${cfgPath}`);
            return;
        }
        FileUtil.copyFile(path.join(this._tempPath, "jsonCfg.bin"), path.join(cfgPath, "jsonCfg.bin"), true, () => {
            Tip.info(`导表成功!`);
        });
    }
    private _exportTs(worksheetList: Map<string, XLSX.WorkSheet>): boolean {
        const gameName = this._bundleName.charAt(0).toUpperCase() + this._bundleName.slice(1);
        const dtsPath = path.resolve(this._proPath, gameName);
        if (!fs.existsSync(dtsPath)) {
            Tip.error(`找不到目录:${dtsPath}`);
            return false;
        }
        let content = "//自动生成，请勿修改！\n\n";
        content += `declare namespace CfgInterface {\n`;
        // console.log("_exportTs");
        worksheetList.forEach((worksheet, sheetName) => {
            content += "\t/**\n \t* " + sheetName + "\n \t*/\n";
            content += "\tinterface I" + sheetName + " {\n";

            const keyList: unknown[] = []; // 键名
            const noteList: unknown[] = []; // 注释
            const typeList: unknown[] = []; // 类型
            const rowNote = ExcelUtil.getRowInSheet(worksheet, 0) as string[]; // 注释
            // console.log("注释", rowNote);
            const rowType = ExcelUtil.getRowInSheet(worksheet, 2) as string[]; // 类型
            // console.log("类型", rowType);
            const rowKey = ExcelUtil.getRowInSheet(worksheet, 1); // 键名
            // console.log("键名", constrowKey);
            const colNum = rowNote.length;
            for (let c = 0; c < colNum; c++) {
                keyList.push(rowKey[c]);
                rowNote[c] = rowNote[c].trim();
                rowNote[c] = rowNote[c].replace(/[\r\n]/g, "");
                noteList.push(rowNote[c]);
                if ("int" == rowType[c] || "num" == rowType[c]) {
                    rowType[c] = "number";
                } else {
                    rowType[c] = rowType[c];
                }
                typeList.push(rowType[c]);
            }
            for (let c = 0; c < colNum; c++) {
                content += "\t\t" + keyList[c] + ": " + typeList[c] + ", //" + noteList[c] + "\n";
            }
            content += "\t}\n";
        });
        content += "}\n";
        fs.writeFileSync(path.join(dtsPath, `${gameName}CsvInterface.d.ts`), content);
        return true;
    }
}

export const cfgMgr = new ConfigManager();
